home *** CD-ROM | disk | FTP | other *** search
- /*
- * main.c
- * Facility: m4 macro processor
- * by: oz
- */
-
- #include "mdef.h"
- #include "extr.h"
-
- #include <kernel.h>
- #include <signal.h>
- #include <string.h>
- #include <stdlib.h>
- #include <sys/misc.h>
-
- /*
- * m4 - macro processor
- *
- * PD m4 is based on the macro tool distributed with the software
- * tools (VOS) package, and described in the "SOFTWARE TOOLS" and
- * "SOFTWARE TOOLS IN PASCAL" books. It has been expanded to include
- * most of the command set of SysV m4, the standard UN*X macro processor.
- *
- * Since both PD m4 and UN*X m4 are based on SOFTWARE TOOLS macro,
- * there may be certain implementation similarities between
- * the two. The PD m4 was produced without ANY references to m4
- * sources.
- *
- * References:
- *
- * Software Tools distribution: macro
- *
- * Kernighan, Brian W. and P. J. Plauger, SOFTWARE
- * TOOLS IN PASCAL, Addison-Wesley, Mass. 1981
- *
- * Kernighan, Brian W. and P. J. Plauger, SOFTWARE
- * TOOLS, Addison-Wesley, Mass. 1976
- *
- * Kernighan, Brian W. and Dennis M. Ritchie,
- * THE M4 MACRO PROCESSOR, Unix Programmer's Manual,
- * Seventh Edition, Vol. 2, Bell Telephone Labs, 1979
- *
- * System V man page for M4
- *
- * Modification History:
- *
- * Jan 28 1986 Oz Break the whole thing into little
- * pieces, for easier (?) maintenance.
- *
- * Dec 12 1985 Oz Optimize the code, try to squeeze
- * few microseconds out..
- *
- * Dec 05 1985 Oz Add getopt interface, define (-D),
- * undefine (-U) options.
- *
- * Oct 21 1985 Oz Clean up various bugs, add comment handling.
- *
- * June 7 1985 Oz Add some of SysV m4 stuff (m4wrap, pushdef,
- * popdef, decr, shift etc.).
- *
- * June 5 1985 Oz Initial cut.
- *
- * Implementation Notes:
- *
- * [1] PD m4 uses a different (and simpler) stack mechanism than the one
- * described in Software Tools and Software Tools in Pascal books.
- * The triple stack nonsense is replaced with a single stack containing
- * the call frames and the arguments. Each frame is back-linked to a
- * previous stack frame, which enables us to rewind the stack after
- * each nested call is completed. Each argument is a character pointer
- * to the beginning of the argument string within the string space.
- * The only exceptions to this are (*) arg 0 and arg 1, which are
- * the macro definition and macro name strings, stored dynamically
- * for the hash table.
- *
- * . .
- * | . | <-- sp | . |
- * +-------+ +-----+
- * | arg 3 ------------------------------->| str |
- * +-------+ | . |
- * | arg 2 --------------+ .
- * +-------+ |
- * * | | |
- * +-------+ | +-----+
- * | plev | <-- fp +---------------->| str |
- * +-------+ | . |
- * | type | .
- * +-------+
- * | prcf -----------+ plev: paren level
- * +-------+ | type: call type
- * | . | | prcf: prev. call frame
- * . |
- * +-------+ |
- * | <----------+
- * +-------+
- *
- * [2] We have three types of null values:
- *
- * nil - nodeblock pointer type 0
- * null - null string ("")
- * NULL - Stdio-defined NULL
- *
- */
-
- ndptr hashtab[HASHSIZE]; /* hash table for macros etc. */
- char buf[BUFSIZE]; /* push-back buffer */
- char *bp = buf; /* first available character */
- char *endpbb = buf+BUFSIZE; /* end of push-back buffer */
- stae mstack[STACKMAX+1]; /* stack of m4 machine */
- char strspace[STRSPMAX+1]; /* string space for evaluation */
- char *ep = strspace; /* first free char in strspace */
- char *endest= strspace+STRSPMAX;/* end of string space */
- int sp; /* current m4 stack pointer */
- int fp; /* m4 call frame pointer */
- File infile[MAXINP]; /* input file stack */
- File outfile[MAXOUT]; /* diversion array */
- FILE *active; /* active output file pointer */
- char *m4temp; /* filename for diversions */
- char *m4divnum; /* pointer to divnum in m4temp */
- char *tempdir; /* directory for temp files */
- int ilevel = 0; /* input file stack pointer */
- int oindex = 0; /* diversion index.. */
- char *null = ""; /* as it says.. just a null.. */
- char *m4wraps = ""; /* m4wrap string default.. */
- char lquote = LQUOTE; /* left quote character (`) */
- char rquote = RQUOTE; /* right quote character (') */
- char scommt = SCOMMT; /* start character for comment */
- char ecommt = ECOMMT; /* end character for comment */
- struct keyblk keywrds[] = { /* m4 keywords to be installed */
- "include", INCLTYPE,
- "sinclude", SINCTYPE,
- "define", DEFITYPE,
- "defn", DEFNTYPE,
- "divert", DIVRTYPE,
- "expr", EXPRTYPE,
- "eval", EXPRTYPE,
- "substr", SUBSTYPE,
- "ifelse", IFELTYPE,
- "ifdef", IFDFTYPE,
- "len", LENGTYPE,
- "incr", INCRTYPE,
- "decr", DECRTYPE,
- "dnl", DNLNTYPE,
- "changequote", CHNQTYPE,
- "changecom", CHNCTYPE,
- "index", INDXTYPE,
- #ifdef EXTENDED
- "paste", PASTTYPE,
- "spaste", SPASTYPE,
- #endif
- "popdef", POPDTYPE,
- "pushdef", PUSDTYPE,
- "dumpdef", DUMPTYPE,
- "shift", SHIFTYPE,
- "translit", TRNLTYPE,
- "undefine", UNDFTYPE,
- "undivert", UNDVTYPE,
- "divnum", DIVNTYPE,
- "maketemp", MKTMTYPE,
- "errprint", ERRPTYPE,
- "m4wrap", M4WRTYPE,
- "m4exit", EXITTYPE,
- "syscmd", SYSCTYPE,
- "sysval", SYSVTYPE,
- "arm", MACRTYPE,
- };
-
- #define MAXKEYS (sizeof(keywrds)/sizeof(struct keyblk))
-
- /* Internal routines */
-
- static void initkwds (void);
- static void initm4 (void);
- static ndptr inspect (char *tp);
- static void macro (void);
-
- int main (int argc, char *argv[])
- {
- register int c;
- register int n;
- int dirlen;
- char *p;
-
- _kernel_osfile_block dummy;
-
- if (signal(SIGINT, SIG_IGN) != SIG_IGN)
- signal(SIGINT, onintr);
-
- #ifdef NONZEROPAGES
- initm4();
- #endif
-
- initkwds();
-
- tempdir = getenv("Tmp$Root");
-
- if (tempdir == NULL)
- {
- /* Does "&.Tmp" exist? */
- if (_kernel_osfile(5,"&.Tmp",&dummy) == 2)
- tempdir = "&.Tmp";
- else
- tempdir = "";
- }
-
- while ((c = getopt(argc, argv, "?D:o:T:U:")) != EOF)
- {
- switch(c)
- {
-
- case 'D': /* define something */
- for (p = optarg; *p; p++)
- if (*p == '=')
- break;
- if (*p)
- *p++ = EOS;
- dodefine(optarg, p);
- break;
-
- case 'o': /* specific output */
- if ( !freopen(optarg, "w", stdout) )
- {
- fprintf(stderr, "M4: Cannot open %s\n", optarg);
- exit(1);
- }
- break;
-
- case 'T': /* temp file dir */
- /* Is it a directory? */
- if (_kernel_osfile(5,optarg,&dummy) != 2)
- error("M4: %s is not a directory",optarg);
- tempdir = optarg;
- break;
-
- case 'U': /* undefine */
- remhash(optarg, TOP);
- break;
-
- case '?':
- default:
- usage();
- }
- }
-
- /* Set up the diversion file name(s) */
-
- dirlen = strlen(tempdir);
-
- if (dirlen != 0)
- ++dirlen; /* add one for the dot */
-
- m4temp = malloc(dirlen+DIVLEN+1);
-
- if (!m4temp)
- error("M4: Heap full");
-
- if (dirlen)
- {
- strcpy(m4temp,tempdir);
- m4temp[dirlen-1] = '.';
- }
-
- strcpy(&m4temp[dirlen],DIVNAM);
-
- m4divnum = &m4temp[dirlen+DIVNUM];
-
- sp = -1; /* stack pointer initialized */
- fp = 0; /* frame pointer initialized */
- active = stdout; /* default active output */
-
- if ( optind >= argc )
- {
- infile[0].name = "-";
- infile[0].fp = stdin;
- infile[0].ptr = 0L;
- macro();
- }
- else
- {
- for ( ; optind < argc; ++optind )
- {
- char *file = argv[optind];
-
- infile[0].name = file;
- infile[0].ptr = 0L;
-
- if (file[0] == '-' && file[1] == '\0')
- infile[0].fp = stdin;
- else
- {
- infile[0].fp = fopen(file,"r");
-
- if ( !infile[0].fp )
- error("M4: Cannot open %s", file);
- }
-
- /* Set the input file stack back to empty */
- ilevel = 0;
- macro();
- }
- }
-
- if (*m4wraps) { /* anything for rundown ?? */
- ilevel = 0; /* in case m4wrap includes.. */
- putback(EOF); /* eof is a must !! */
- pbstr(m4wraps); /* user-defined wrapup act */
- macro(); /* last will and testament */
- }
- else /* default wrap-up: undivert */
- {
- for (n = 1; n < MAXOUT; n++)
- {
- if (outfile[n].ptr)
- getdiv(n);
- }
- }
-
- return 0;
- }
-
- /*
- * macro - the work horse..
- *
- */
- static void macro (void)
- {
- char token[MAXTOK];
- register char *s;
- register int t, l;
- register ndptr p;
- register int nlpar;
-
- forever
- {
- if ((t = gpbc()) == '_' || isalpha(t))
- {
- putback(t);
- if ((p = inspect(s = token)) == nil)
- putstr(s);
- else {
- /*
- * real thing.. First build a call frame:
- *
- */
- pushf(fp); /* previous call frm */
- pushf(p->type); /* type of the call */
- pushf(0); /* parenthesis level */
- fp = sp; /* new frame pointer */
- /*
- * now push the string arguments:
- *
- */
- pushs(p->defn); /* defn string */
- pushs(p->name); /* macro name */
- pushs(ep); /* start next..*/
-
- putback(l = gpbc());
- if (l != LPAREN) { /* add bracks */
- putback(RPAREN);
- putback(LPAREN);
- }
- }
- }
-
- /*
- * End of file - close this file
- * and go up one level in the
- * list of included files.
- */
- else if (t == EOF) {
-
- if (sp > -1)
- error("M4: Unexpected end of input");
-
- /* Have we all the input? */
- if (--ilevel < 0)
- break;
-
- (void) fclose(infile[ilevel+1].fp);
-
- /* Check for stdin... */
- if (INNAME[0] == '-' && INNAME[1] == '\0')
- INFP = stdin;
- else
- {
- /* Reopen the previous file */
- if ( (INFP = fopen(INNAME,"r")) == NULL )
- error("M4: Cannot reopen %s", INNAME);
-
- if ( fseek(INFP, INPTR, SEEK_SET) )
- error("M4: Cannot return to previous "
- "position in %s", INNAME);
- }
-
- continue;
- }
- /*
- * non-alpha single-char token seen..
- * [the order of else if .. stmts is
- * important.]
- *
- */
- else if (t == lquote) { /* strip quotes */
- nlpar = 1;
- do {
- if ((l = gpbc()) == rquote)
- nlpar--;
- else if (l == lquote)
- nlpar++;
- else if (l == EOF)
- error("M4: Missing right quote");
- if (nlpar > 0)
- putchr(l);
- }
- while (nlpar != 0);
- }
-
- else if (sp < 0)
- {
- /* not in a macro at all */
-
- /* Check for comments */
- if (t == scommt)
- {
-
- /* Skip the comment */
- while ((t = gpbc()) != ecommt)
- ;
-
- continue;
- }
-
- /* Output the character */
- if (active != NULL)
- putc(t, active);
- }
-
- else switch(t)
- {
-
- case LPAREN:
- if (PARLEV > 0)
- putchr(t);
- while (isspace(l = gpbc()))
- ; /* skip blank, tab, nl.. */
- putback(l);
- PARLEV++;
- break;
-
- case RPAREN:
- if (--PARLEV > 0)
- putchr(t);
- else { /* end of argument list */
- putchr(EOS);
-
- if (sp == STACKMAX)
- error("M4: Internal stack overflow");
-
- if (CALTYP == MACRTYPE)
- expand((char **)(mstack+fp+1), sp-fp);
- else
- eval((char **)(mstack+fp+1), sp-fp, CALTYP);
-
- ep = PREVEP; /* flush strspace */
- sp = PREVSP; /* previous sp.. */
- fp = PREVFP; /* rewind stack...*/
- }
- break;
-
- case COMMA:
- if (PARLEV == 1) {
- putchr(EOS); /* new argument */
- while (isspace(l = gpbc()))
- ;
- putback(l);
- pushs(ep);
- }
- break;
- default:
- putchr(t); /* stack the char */
- break;
- }
- }
- }
-
-
- /*
- * build an input token..
- * consider only those starting with _ or A-Za-z. This is a
- * combo with lookup to speed things up.
- */
- static ndptr inspect (char *tp)
- {
- register int h = 0;
- register char c;
- register char *name = tp;
- register char *etp = tp+MAXTOK;
- register ndptr p;
-
- while (tp < etp && (isalnum(c = gpbc()) || c == '_'))
- h += (*tp++ = c);
- putback(c);
- if (tp == etp)
- error("M4: Token too long");
- *tp = EOS;
- for (p = hashtab[h%HASHSIZE]; p != nil; p = p->nxtptr)
- if (strcmp(name, p->name) == 0)
- break;
- return(p);
- }
-
- #ifdef NONZEROPAGES
- /*
- * initm4 - initialize various tables. Useful only if your system
- * does not know anything about demand-zero pages.
- *
- */
- static void initm4 (void)
- {
- register int i;
-
- for (i = 0; i < HASHSIZE; i++)
- hashtab[i] = nil;
-
- for (i = 0; i < MAXOUT; i++)
- {
- outfile[i].name = "";
- outfile[i].fp = NULL;
- outfile[i].ptr = 0L;
- }
- }
- #endif
-
- /*
- * initkwds - initialise m4 keywords as fast as possible.
- * This very similar to install, but without certain overheads,
- * such as calling lookup. Malloc is not used for storing the
- * keyword strings, since we simply use the static pointers
- * within keywrds block. We also assume that there is enough memory
- * to at least install the keywords (i.e. malloc won't fail).
- *
- */
- static void initkwds (void)
- {
- register int i;
- register int h;
- register ndptr p;
-
- for (i = 0; i < MAXKEYS; i++) {
- h = hash(keywrds[i].knam);
- p = (ndptr) malloc(sizeof(struct ndblock));
- p->nxtptr = hashtab[h];
- hashtab[h] = p;
- p->name = keywrds[i].knam;
- p->defn = null;
- p->type = keywrds[i].ktyp | STATIC;
- }
- }
-